import SocketServer,BaseHTTPServer
from DB.Library import *
from Utils.Settings import *
from Utils.PListParser import *
from StringIO import StringIO
import shutil, threading, mimetypes, posixpath,os,urllib,time
from qt import *

class ServerHandler(BaseHTTPServer.BaseHTTPRequestHandler):

	server_version = "Lsongs/1.0"
	importLock = threading.Lock()

	def log_message(self, format, *args):
		pass

	def allowWrites(self):
		return Utils.Settings.settings().get('Share Allow Writes',False)
		
	def do_GET(self):
		#print "handling GET request",self.path
		if self.path=='/':
			self.protocol()
		elif self.path.startswith('/playlist?'):
			self.playlist(self.path)
		elif self.path.startswith('/playlistIDs?'):
			self.playlistIDs(self.path)
		elif self.path.startswith("/playlists"):
			self.playlists()
		elif self.path.startswith('/stream?'):
			self.stream(self.path)
		elif self.path.startswith('/createPlaylist?'):
			self.createPlaylist(self.path)
		else:
			self.emitError("Unknown resource")
	
	def emit(self,f,len,contentType = 'text/xml'):
		self.send_response(200)
		self.send_header("Content-type",contentType)
		self.send_header("Content-length",len)
		self.end_headers()
		shutil.copyfileobj(f,self.wfile)
		f.close()

	def emitString(self,s,contentType = 'text/xml'):
		self.send_response(200)
		self.send_header("Content-type",contentType)
		self.send_header("Content-length",len(s))
		self.end_headers()
		self.wfile.write(s)
	
	def emitPList(self,plist):
		self.emitString(PListWriter().unparseToString([plist]))
		
	def emitError(self,error = "File not found"):
		self.send_error(404,error)
	
	def protocol(self):
		self.emitPList({'Version':'1.0','Protocol':'1.0'})

	def playlists(self):
		try:
			plist = Library.mainLibrary().getRemotePList(False)
			plist['Version'] = '1.0'
			self.emitPList(plist)
		except: self.emitError("Failure to fetch playlists")
	
	def playlist(self,path):
		try:
			s = path.split('?')
			id = int(s[1])
			lib = Library.mainLibrary()
			playlist = lib.playlistWithID(id)
			if playlist:
				plist = playlist.getRemotePList(True)
				plist['Version'] = '1.0'
				self.emitPList(plist)
			else: self.emitError("Can't find playlist with ID=%d"% id)
		except: self.emitError("Parse error")

	def playlistIDs(self,path):
		try:
			s = path.split('?')
			id = int(s[1])
			lib = Library.mainLibrary()
			playlist = lib.playlistWithID(id)
			if playlist:
				plist = playlist.getPList(True)
				plist['Version'] = '1.0'
				self.emitPList(plist)
			else: self.emitError("Can't find playlist with ID=%d"% id)
		except: self.emitError("Parse error")

	def createPlaylist(self,path):
		if self.allowWrites():
			try:
				s = path.split('?')
				playlistName = urllib.unquote(s[1])
				#print "creating playlist",playlistName
				lib = Library.mainLibrary()
				playlist = lib.playlistWithName(playlistName)
				if playlist==None:
					playlist = lib.addNewPlaylist(playlistName)
					playlist.shared = True
				plist = {'Version':'1.0','Playlist ID':playlist.playlistID}
				self.emitPList(plist)
			except: self.emitError("Failure to create playlist")
		else: self.send_error(403,"Write access denied")
			
	def stream(self,path):
		#print "attempting to stream",path
		if 1:
			s = path.split('?')
			id = int(s[1])
			lib = Library.mainLibrary()
			track = lib.trackWithTrackID(id)
			if track:
				location = track.location
				f = open(location,"rb")
				self.send_response(200)
				ctype = self.guessType(location)
				#print "streaming type",ctype
				self.send_header("Content-type", ctype)
				len = os.fstat(f.fileno())[6]
				#print "file length",len
				self.send_header("Content-Length", str(len))
				self.end_headers()
				try: shutil.copyfileobj(f,self.wfile)
				except: pass
				f.close()
			else: self.emitError("Can't find track with ID=%d" % id)
		#except: self.emitError("Parse error")

	def do_HEAD(self):
		pass

	def do_POST(self):
		if self.path.startswith("/haveTracks"):
			self.haveTracks()
		else:
			self.emitError("Unknown resource")

	# takes an array of track items, returns array of booleans
	# whether or not we have these tracks in our database
	def haveTracks(self):
		#print "testing for tracks"
		try:
			data = ""
			while 1:
				line = self.rfile.readline()
				if len(line)<=1: break
				data = data + line
			plist = PListReader().parseString(data)[0]
			result = []
			tracks = plist['Tracks']
			for trackPList in tracks:
				track = FileTrack() # to get shortName
				track.setPList(trackPList)
				otherTrack = Library.mainLibrary().findTrackLike(track)
				if otherTrack: result.append({'Track ID':track.trackID,'Found':True,'Local Track ID':otherTrack.trackID})
				else: result.append({'Track ID':track.trackID,'Found':False})
			plist = {'Version':'1.0','Tracks':result}
			self.emitPList(plist)
		except: self.send_error(500,"Malformed request")
	
	def guessType(self, path):
		base, ext = posixpath.splitext(path)
		if ext in self.extensions_map:
			return self.extensions_map[ext]
		ext = ext.lower()
		if ext in self.extensions_map:
			return self.extensions_map[ext]
		else:
			return self.extensions_map['']

	extensions_map = mimetypes.types_map.copy()
	extensions_map.update({
		'': 'application/octet-stream', # Default
		'.mp3': 'audio/mp3',
		'.ogg': 'audio/vorbis',
		'.wav': 'audio/wav',
		'.aiff':'audio/aiff',
		'.aifc':'audio/aiff'
		})

	def do_PUT(self):
		#print "handling PUT request",self.path
		if self.path.startswith("/uploadFast?"):
			self.uploadFast(self.path)
		elif self.path.startswith('/upload'):
			self.upload()
		else:
			self.emitError("Unknown resource")
	
	def upload(self):
		if self.allowWrites():
			print "server: receiving upload at",time.time()
			data = None
			try:
				size  = int(self.headers['content-length'])
				#print "uploader receiving",size,"bytes"
				data = self.rfile.read(size)
			except: pass
			self.send_response(200)
			self.end_headers()
			try:
				plist = PListReader().parseString(data)
				if plist:
					plist = plist[0]
					plist = plist['Track']
					print "server: decoding track",time.time()
					data = base64.decodestring(plist['Data'])
					print "server: decoded track",time.time()
					del plist['Data']
					track = Track()
					track.setPList(plist)
					(path,shortName) = os.path.split(track.location)
					#print "got",shortName
					fileName = "/tmp/%s" % shortName
					open(fileName,"wb").write(data)
					library = Library.mainLibrary()
					playlistNames = plist['Playlists']
					playlists = []
					for playlistName in playlistNames:
						playlist = library.playlistWithName(playlistName)
						if playlist==None:
							playlist = library.addNewPlaylist(playlistName)
							playlist.shared = True
						if not playlist.master:
							playlists.append(playlist)
					print "server: importing track",time.time()
					self.importLock.acquire()
					track = library.importFile(fileName,True,playlists)
					try: os.remove(fileName)
					except: pass
					self.importLock.release()
					print "server: finished upload at",time.time()
			except: pass
		else: self.send_error(403,"Write access denied")

	def uploadFast(self,path):
		if self.allowWrites():
			s = path.split('?')
			shortName = urllib.unquote(s[1])
			#print "server saving file to",shortName
			#print "server: receiving upload at",time.time()
			data = None
			try:
				size  = int(self.headers['content-length'])
				#print "uploader receiving",size,"bytes"
				data = self.rfile.read(size)
				#print "got",shortName
				fileName = "/tmp/%s" % shortName
				open(fileName,"wb").write(data)
				library = Library.mainLibrary()
				#print "server: importing track",time.time()
				self.importLock.acquire()
				track = library.importFile(fileName,True,None)
				try: os.remove(fileName)
				except: pass
				self.importLock.release()
				#print "server: finished upload at",time.time()
			except: pass
			self.send_response(200)
			self.end_headers()
		else: self.send_error(403,"Write access denied")

class LsongsHTTPServer(SocketServer.ThreadingMixIn,BaseHTTPServer.HTTPServer,QObject):
	allow_reuse_address = True
	def __init__(self):
		QObject.__init__(self)
		serverAddress = ('',9001)
		try: BaseHTTPServer.HTTPServer.__init__(self,serverAddress,LsongsServerHandler)
		except: pass

	def process_request_thread(self, request, client_address):
		try:
			self.finish_request(request, client_address)
			self.close_request(request)
		except:
			self.handle_error(request, client_address)
			self.close_request(request)

	def process_request(self, request, client_address):
		"""Start a new thread to process the request."""
		import threading
		t = threading.Thread(target = self.process_request_thread,
			args = (request, client_address))
		t.start()

class LsongsServer(QThread):
	def __init__(self):
		QThread.__init__(self)
		print "starting Lsongs server"
		self.server = LsongsHTTPServer()
		self.halt = False
		self.start()
	
	def killTasks(self):
		print "killing server"
		self.halt = True
		#self.terminate()
		self.wait()
		print "killed"

	def run(self):
		self.server.socket.settimeout(2)
		while not self.halt:
			try: self.server.handle_request()
			except: pass
		#self.server.socket.bind(('',0))
		self.server.socket.close()
		self.server = None
		#print "exiting server loop"
